HDU 4606 Occupy Cities (计算几何、最短路、最小路径覆盖)
题意:
给出$n\le 100$个城市需要去占领,有$m\le 100$条线段是障碍物,有$p\le 100$个士兵可以用
占领城市有个先后顺序,每个士兵有个背包,占领城市之后,仅能补给一次背包
问背包容量最少是多少,可以用这$p$个士兵完成任务,起点任意
分析:
枚举城市以及障碍的顶点,需要特判是某个障碍的$2$个端点的情况,求一下距离,判断是否与线段相交
然后$floyd$预处理最短路
二分背包容量,根据占领的先后顺序建边,跑二分图最小路径覆盖判断是否$p$个士兵可以占领
代码:
//
// Created by TaoSama on 2016-03-01
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 3e2 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const double EPS = 1e-8;
int sgn(double x) {
return x < -EPS ? -1 : x > EPS;
}
int n, m, p, schedule[N];
double d[N][N];
struct Point {
double x, y;
void read() {scanf("%lf%lf", &x, &y);}
Point operator-(const Point& p) {
return {x - p.x, y - p.y};
}
double operator*(const Point& p) {
return x * p.x + y * p.y;
}
double operator^(const Point& p) {
return x * p.y - y * p.x;
}
double length() {
return sqrt(*this **this);
}
} ps[N];
bool segmentProperIntersection(Point a1, Point a2, Point b1, Point b2) {
double c1 = (a2 - a1) ^ (b1 - a1), c2 = (a2 - a1) ^ (b2 - a1);
double c3 = (b2 - b1) ^ (a1 - b1), c4 = (b2 - b1) ^ (a2 - b1);
return sgn(c1) * sgn(c2) < 0 && sgn(c3) * sgn(c4) < 0;
}
bool vis[N];
int match[N];
vector<int> G[N];
bool dfs(int u) {
for(int v : G[u]) {
if(vis[v]) continue;
vis[v] = true;
if(!match[v] || dfs(match[v])) {
match[v] = u;
return true;
}
}
return false;
}
bool check(double x) {
for(int i = 1; i <= n; ++i) G[i].clear();
for(int i = 1; i <= n; ++i) {
int u = schedule[i];
for(int j = i + 1; j <= n; ++j) {
int v = schedule[j];
if(sgn(x - d[u][v]) >= 0) G[u].push_back(v);
}
}
int matches = 0;
memset(match, 0, sizeof match);
for(int i = 1; i <= n; ++i) {
memset(vis, 0, sizeof vis);
matches += dfs(i);
}
return n - matches <= p;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
while(t--) {
scanf("%d%d%d", &n, &m, &p);
for(int i = 1; i <= n; ++i) ps[i].read();
for(int i = 1; i <= m; ++i) {
ps[n + i].read();
ps[n + m + i].read();
}
for(int i = 1; i <= n; ++i) scanf("%d", schedule + i);
for(int i = 1; i <= n + 2 * m; ++i)
for(int j = 1; j <= n + 2 * m; ++j)
d[i][j] = i == j ? 0 : 1e18;
for(int i = 1; i <= n + 2 * m; ++i) {
for(int j = i + 1; j <= n + 2 * m; ++j) {
if(i > n && j - i == m) continue; //same barrier's two ends
bool can = true;
for(int k = 1; k <= m; ++k) {
if(segmentProperIntersection(ps[i], ps[j], ps[n + k], ps[n + m + k])) {
can = false;
break;
}
}
if(can) d[i][j] = d[j][i] = (ps[i] - ps[j]).length();
}
}
//Floyd
for(int k = 1; k <= n + 2 * m; ++k)
for(int i = 1; i <= n + 2 * m; ++i)
for(int j = 1; j <= n + 2 * m; ++j)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
double l = 0, r = 1e5;
for(int i = 1; i <= 100; ++i) {
double m = (l + r) / 2;
if(check(m)) r = m;
else l = m;
}
printf("%.2f\n", l);
}
return 0;
}